package fr.ece.ing4.si.MonteCarloConcur;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import fr.ece.ing4.si.MonteCarloGUI.myGUI;
import fr.ece.ing4.si.MonteCarloSet.Set;

//this function calculate a single value
public class MonteCarloConcur {
	public Double MonteCarloStandardOption(String CallPutFlag, Double S, Double X, Double T, Double r, Double b, Double v, Integer nSteps, Integer nSimulations) 
			throws InterruptedException, ExecutionException {
		
		Double dt, St;
		Double Sum, Drift, vSqrdt;
		Integer j, z;
		Random rand = new Random();
	
		dt = T/nSteps;
		Drift = (b-(v*v)/2)*dt;
		vSqrdt = v*(Math.sqrt(dt));
		Sum = 0.0;
		z = 0;
		
		if(CallPutFlag.compareTo("c") == 0) {
			z = 1;
		}
		else if (CallPutFlag.compareTo("p") == 0) {
			z = -1;
		}
		
		St = S;
		for(j=0;j<nSteps;j++) {
			St = St*Math.exp(Drift+vSqrdt*rand.nextGaussian());
		}
		
		Sum = Sum + Math.max(z*(St-X), 0);
		
		return Sum;
	}
	
	//This function calculate the total value
	public Double totalMonteCarlo(final String CallPutFlag, final Double S, final Double X, final Double T, final Double r, final Double b, final Double v, final Integer nSteps, final Integer nSimulations, int poolSize) {
		double resultMC2 = 0.0;
		double resultMC = 0.0;
		
		try {
			final List<Callable<Double>> partitions = new ArrayList<Callable<Double>>();
			
			//for each simulation, compute the montecarlo function in one thread
			//and retrieve all the single values
			for(int i=0; i<nSimulations;i++) {	
				partitions.add(new Callable<Double>() {
					public Double call() throws Exception {							
						return (new MonteCarloConcur().MonteCarloStandardOption(CallPutFlag, 
								S, X, T, r, b, v, nSteps, nSimulations));
					}
				});
			}
			
			final ExecutorService executorPool = Executors
					.newFixedThreadPool(poolSize);
			final List<Future<Double>> valueOfStocks = executorPool.invokeAll(
					partitions, 10000, TimeUnit.SECONDS);
			
			//calculate the final value
			for (final Future<Double> valueOfAStock : valueOfStocks)
				resultMC2 += valueOfAStock.get();
			
			resultMC = Math.exp(-r * T) * (resultMC2/nSimulations);
			
			executorPool.shutdown();
		
		} catch (InterruptedException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (ExecutionException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
		//Return it to the GUI
		return resultMC;
	}
	
	//this function retrieve all the sets in the txt file
	public static ArrayList<Set> readSets() throws IOException {
	    Set newSet;
		final BufferedReader reader = 
	      new BufferedReader(new FileReader("MonteCarlo_values.txt"));
	    
		//list of sets
	    final ArrayList<Set> Sets = new ArrayList<Set>();
	    
	    String set = null;
	    //retrieve and stock in a new set all the lines in the file
	    while((set = reader.readLine()) != null) {
	    	//cut the line to differenciate the arguments
	      final String[] stockSet = set.split(",");
	      newSet = new Set(Double.valueOf(stockSet[0]), 
	    		  			Double.valueOf(stockSet[1]), 
	    		  			Double.valueOf(stockSet[2]), 
	    		  			Double.valueOf(stockSet[3]), 
	    		  			Double.valueOf(stockSet[4]), 
	    		  			Double.valueOf(stockSet[5]), 
	    		  			Integer.valueOf(stockSet[6]), 
	    		  			Integer.valueOf(stockSet[7]));      
	      Sets.add(newSet); 
	    }
	    
	    return Sets;    
	  }
	
	//this function is the main
	public static void main(final String[] args) 
		throws ExecutionException, IOException, InterruptedException {
			
			//retrieve the sets
			ArrayList<Set> Sets = readSets();
			
			//launch the GUI 
			myGUI GUI = new myGUI(Sets);
			GUI.setVisible(true);
		}
	
}
